package model
{
	
	import manager.*;
	import remoting.*;
	import vo.CharacterVO;
	import vo.CharacterVOServer;
	import log.Logger;
	
	/**
	 * -- CharacterPx --
	 * @desc	used to manage character's data
	 * @author	Aqua (built on 2011-11-09)
	 */
	public class CharacterPx extends BasePx
	{
		//-----------------//
		//     Publics     //
		//-----------------//
		
		/**************************************************************************
		 * -- init --
		 * @desc		initialize proxy
		 *************************************************************************/
		public override function init():void
		{
			_userPx = ProxyMgr.instance.getProxy(UserPx.NAME) as UserPx;
			_charaXML = ResourceMgr.instance.getRes("xml_characters") as XML;
			_listInited = false;
		}
		
		/**************************************************************************
		 * -- (getter) inited --
		 * @desc		get if character list is inited
		 * @return		if character list is inited
		 *************************************************************************/
		public function get inited():Boolean
		{
			return _listInited;
		}
		
		/**************************************************************************
		 * -- initCharaList --
		 * @desc		get all user's character from database, then push them
		 * 				into character list
		 * @param		(initNullList) only get character list from database when
		 * 				this param is false
		 *************************************************************************/
		public function initCharaList(initNullList:Boolean):void
		{
			var count:int;
			var charaVO:CharacterVO;
			
			_charaList = [];
			_numTeamMember = 0;
			
			if (initNullList)
			{
				_listInited = true;
				return;
			}
			
			Server.call(Method.GET_USER_CHARA, onResult, { "uid":_userPx.userVO.uid });
			
			function onResult(res:Object):void
			{
				var cid:int;
				
				count = res.count;
				
				for each (cid in res.list)
				{
					getCharaData(cid, response);
				}
				if (0 == count)
				{
					_listInited = true;
				}
			}
			
			function response(charaVO:CharacterVO):void
			{
				if (NOT_IN_TEAM != charaVO.teamStatus)
				{
					_numTeamMember ++;
				}
				
				_charaList.push(charaVO);
				Logger.log("init chara list: " + charaVO.cid + " - " + getCharaName(charaVO.id));
				
				if (0 == -- count)
				{
					_listInited = true;
				}
			}
		}
		
		/**************************************************************************
		 * -- createChara --
		 * @desc		create a character with initial data which is defined in xml
		 * @param		(id) id of the character
		 * @param		(teamStat) status about team
		 * @param		(response) response function when creating is finished
		 *************************************************************************/
		public function createChara(id:String, teamStat:int = 0, response:Function = null):void
		{
			var charaVO:CharacterVO;
			var xml:XML;
			
			charaVO = new CharacterVO();
			xml = getXML(id);
			
			charaVO.uid			= _userPx.userVO.uid;
			charaVO.id			= id;
			charaVO.lv			= xml.initial_level;
			charaVO.hp			= xml.initial_hp;
			charaVO.exp			= 0;
			charaVO.midAttack	= xml.initial_mid_attack;
			charaVO.lowAttack	= xml.initial_low_attack;
			charaVO.midDefense	= xml.initial_mid_defense;
			charaVO.lowDefense	= xml.initial_low_defense;
			charaVO.speed		= xml.speed;
			charaVO.teamStatus	= NOT_IN_TEAM;
			charaVO.position	= -1;
			
			buildCharaData(charaVO, onResult);
			
			function onResult(cvo:CharacterVO):void
			{
				// add new character to user's character list directly because 
				// now client doesn't need to create characters for other users
				addChara(cvo);
			}
		}
		
		/**************************************************************************
		 * -- getGrownChara --
		 * @desc		get a character with random grown ability (this character
		 * 				won't be saved to database)
		 * @param		(id) id of the character
		 * @param		(lv) specify a level
		 * @return		data of the character
		 *************************************************************************/
		public function getGrownChara(id:String, lv:int):CharacterVO
		{
			return null;
		}
		
		/**************************************************************************
		 * -- addChara --
		 * @desc		add a character to user's character list
		 * @param		(charaVO) data of the character
		 *************************************************************************/
		public function addChara(charaVO:CharacterVO):void
		{
			var cvo:CharacterVO;
			
			for each (cvo in _charaList)
			{
				if (NOT_IN_TEAM != cvo.teamStatus)
				{
					if (++ _numTeamMember > 3)
					{
						throw new Error("Too many team members!");
					}
				}
				if (cvo.cid == charaVO.cid)
				{
					throw new Error("Character already exists!");
				}
			}
			
			cvo = new CharacterVO();
			copyVO(charaVO, cvo);
			_charaList.push(cvo);
			Logger.log("add character: " + cvo.cid + " - " + cvo.id + " - " + getCharaName(cvo.id));
		}
		
		/**************************************************************************
		 * -- removeChara --
		 * @desc		remove a character from user's character list
		 * @param		(cid) cid of the character
		 *************************************************************************/
		public function removeChara(cid:int):void
		{
			var index:String;
			var cvo:CharacterVO;
			
			for (index in _charaList)
			{
				cvo = _charaList[index];
				
				if (cvo.cid == cid)
				{
					_charaList.splice(index, 1);
					Logger.log("remove character: " + cid + " - " + cvo.id + " - " + getCharaName(cvo.id));
					return;
				}
			}
			
			throw new Error("Character doesn't exist!");
		}
		
		/**************************************************************************
		 * -- getCharaData --
		 * @desc		get character's data from database
		 * @param		(cid) cid of character
		 * @param		(response) response function when data got
		 *************************************************************************/
		public function getCharaData(cid:int, response:Function):void
		{
			Server.call(Method.GET_CHARA_DATA, onResult, { "cid":cid } );
			
			function onResult(res:Object):void
			{
				var charaVO:CharacterVO;
				
				charaVO = new CharacterVO();
				copyVO(res, charaVO);
				response(charaVO);
			}
		}
		
		/**************************************************************************
		 * -- (getter) charaNum --
		 * @desc		get the amount of user's characters
		 * @return		amount of characters
		 *************************************************************************/
		public function get charaNum():int
		{
			return _charaList.length;
		}
		
		/**************************************************************************
		 * -- getCharaName --
		 * @desc		get a character's name
		 * @param		(id) id of the character
		 * @param		(isShort) get short name or full name
		 * @return		name of character
		 *************************************************************************/
		public function getCharaName(id:String, isShort:Boolean = false):String
		{
			var name:String;
			var xml:XML;
			
			xml = getXML(id);
			name = (isShort ? xml.short_name : xml.full_name);
			
			return name;
		}
		
		/**************************************************************************
		 * -- getCharaByCid --
		 * @desc		get character's data by cid
		 * @param		(cid) cid of character
		 * @return		data of character
		 *************************************************************************/
		public function getCharaByCid(cid:int):CharacterVO
		{
			var charaVO:CharacterVO;
			
			for each (charaVO in _charaList)
			{
				if (charaVO.cid == cid)
				{
					return charaVO;
				}
			}
			
			return null;
		}
		
		/**************************************************************************
		 * -- getCharaByIndex --
		 * @desc		get character's data by index
		 * @param		(index) index of character
		 * @return		data of character
		 *************************************************************************/
		public function getCharaByIndex(index:int):CharacterVO
		{
			if (! _charaList[index])
			{
				throw new Error("Wrong character index!");
			}
			
			return _charaList[index];
		}
		
		/**************************************************************************
		 * -- getCharaByPos --
		 * @desc		get a character by position
		 * @param		(posIndex) index of position
		 * @return		character's data
		 *************************************************************************/
		public function getCharaByPos(posIndex:int):CharacterVO
		{
			var charaVO:CharacterVO;
			
			for each (charaVO in _charaList)
			{
				if (charaVO.position == posIndex)
				{
					return charaVO;
				}
			}
			
			return null;
		}
		
		/**************************************************************************
		 * -- setCharaPos --
		 * @desc		set a character to a specified position
		 * @param		(charaIndex) index of the character
		 * @param		(posIndex) index of position
		 *************************************************************************/
		public function setCharaPos(charaIndex:int, posIndex:int):void
		{
			var charaVO:CharacterVO;
			var cvo:CharacterVO;
			var oldPos:int;
			
			charaVO = _charaList[charaIndex];
			oldPos = charaVO.position;
			
			if (! charaVO || charaVO.position == posIndex)
			{
				return;
			}
			
			// find if there is a character already at this position
			for each (cvo in _charaList)
			{
				if (cvo.position == posIndex)
				{
					cvo.position = oldPos;
					
					if (-1 == oldPos)
					{
						cvo.teamStatus = NOT_IN_TEAM;
						_numTeamMember --;
					}
					
					updateCharaData(cvo);
					break;
				}
			}
			
			// there're up to 3 members in a team
			if (-1 == oldPos && _numTeamMember >= 3)
			{
				return;
			}
			
			charaVO.position = posIndex;
			updateCharaData(charaVO);
			
			if (NOT_IN_TEAM == charaVO.teamStatus)
			{
				charaVO.teamStatus = IN_TEAM;
				_numTeamMember ++;
			}
			
			Logger.log("set chara pos: chara - " + charaVO.id + " position - " + String(posIndex));
		}
		
		
		//------------------//
		//     Privates     //
		//------------------//
		
		/**************************************************************************
		 * -- getXML --
		 * @desc		get xml data for a character
		 * @param		(id) id of the character
		 *************************************************************************/
		private function getXML(id:String):XML
		{
			var xml:XML;
			
			for each (xml in _charaXML.character)
			{
				if (xml.id == id)
				{
					return xml;
				}
			}
			
			return null;
		}
		
		/**************************************************************************
		 * -- buildCharaData --
		 * @desc		insert new character to database
		 * @param		(charaVO) data of the character
		 * @param		(response) response function when operation is finished
		 *************************************************************************/
		private function buildCharaData(charaVO:CharacterVO, response:Function):void
		{
			var charaVOServer:CharacterVOServer;
			charaVOServer = new CharacterVOServer();
			charaVOServer.SetValue(charaVO);
			
			Server.call(Method.BUILD_CHARA_DATA, onResult, charaVOServer);
			
			function onResult(res:Object):void
			{
				var cvo:CharacterVO;
				
				cvo = new CharacterVO();
				copyVO(res, cvo);
				response(cvo);
			}
		}
		
		/**************************************************************************
		 * -- updateCharaData --
		 * @desc		update character's data to database
		 * @param		(charaVO) data of the character
		 *************************************************************************/
		private function updateCharaData(charaVO:CharacterVO):void
		{
			
		}
		
		/**************************************************************************
		 * -- copyVO --
		 * @desc		copy data from one CharacterVO to another
		 * @param		(vo1) data source
		 * @param		(vo2) target
		 *************************************************************************/
		private function copyVO(vo1:Object, vo2:CharacterVO):void
		{
			vo2.cid			= vo1.cid;
			vo2.uid			= vo1.uid;
			vo2.id			= vo1.id;
			vo2.lv			= vo1.lv;
			vo2.hp			= vo1.hp;
			vo2.exp			= vo1.exp;
			vo2.midAttack	= vo1.midAttack;
			vo2.lowAttack	= vo1.lowAttack;
			vo2.midDefense	= vo1.midDefense;
			vo2.lowDefense	= vo1.lowDefense;
			vo2.speed		= vo1.speed;
		}
		
		
		public static const NAME:String	= "CharacterPx";
		
		// describe character's status about in team or not
		public const NOT_IN_TEAM	:int	= 0;
		public const IN_TEAM		:int	= 1;
		public const CAPTAIN		:int	= 2;
		
		// describe character's position in battle
		public const FRONT		:int	= 0;
		public const MIDDLE		:int	= 1;
		public const BACK		:int	= 2;
		
		private var _userPx			:UserPx;
		private var _listInited		:Boolean;
		private var _charaXML		:XML;
		private var _charaList		:Array;
		private var _numTeamMember	:int;
	}
	
}